package com.atguigu.springcloud.alibaba;


import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.HostNameUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.springcloud.alibaba.entity.ClusterGroupEntity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import java.util.*;

//import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;

/**
 * @auther zzyy
 * @create 2020-02-24 16:26
 */
@SpringBootApplication
@EnableDiscoveryClient
//@EnableDubbo
public class MainApp8401 {

    String APP_NAME = "sentinel-test";
    String remoteAddress = "47.105.183.24:8848";
    String groupId = "DEFAULT_GROUP";
    String namespace = "public";

    String clusterMapDataId = APP_NAME + "-cluster-map";
    String configDataId = APP_NAME + "-cluster-client-config";
    String flowDataId = APP_NAME + "-FlowRule";
    String namespaceSetDataId = "cluster-server-namespace-set";
    Properties properties;

    {
        properties = new Properties();
        properties.setProperty("serverAddr", remoteAddress);
        properties.setProperty("namespace", namespace);
    }


    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
        // Register client dynamic rule data source.
//        new MainApp8401().initDynamicRuleProperty();

        // Register token client related data source.
        // Token client common config:
//        initClientConfigProperty();
        // Token client assign config (e.g. target token server) retrieved from assign map:

//
//
//        new MainApp8401().registerNamespaceSetProperty();
//
//        // Register token server related data source.
//        // Register dynamic rule data source supplier for token server:
//        new MainApp8401().registerClusterRuleSupplier();
////        // Token server transport config extracted from assign map:
//        new MainApp8401().initClientServerAssignProperty();
//        new MainApp8401().initStateProperty();
//        new MainApp8401().initServerTransportConfigProperty();

//
//        // Init cluster state property for extracting mode from cluster map data source.


    }


    private void initDynamicRuleProperty() {
        ReadableDataSource<String, List<FlowRule>> ruleSource = new NacosDataSource<>(properties, groupId,
                flowDataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
        }));
        FlowRuleManager.register2Property(ruleSource.getProperty());

//        ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(remoteAddress, groupId,
//            paramDataId, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
//        ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
    }

//    private void initClientConfigProperty() {
//        ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(remoteAddress, groupId,
//            configDataId, source -> JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {}));
//        ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
//    }

    private void initServerTransportConfigProperty() {
        ReadableDataSource<String, ServerTransportConfig> serverTransportDs = new NacosDataSource<>(properties, groupId,
                clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {
            });
            return Optional.ofNullable(groupList)
                    .flatMap(this::extractServerTransportConfig)
                    .orElse(null);
        });
        ClusterServerConfigManager.registerServerTransportProperty(serverTransportDs.getProperty());
    }

    private void registerClusterRuleSupplier() {
        // Register cluster flow rule property supplier which creates data source by namespace.
        // Flow rule dataId format: ${namespace}-flow-rules
        ClusterFlowRuleManager.setPropertySupplier(namespace -> {
            ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(properties, groupId,
                    namespace + "-FlowRule", source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
            }));
            return ds.getProperty();
        });
        // Register cluster parameter flow rule property supplier which creates data source by namespace.
//        ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
//            ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(properties, groupId,
//                    namespace + DemoConstants.PARAM_FLOW_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
//            return ds.getProperty();
//        });
    }

    private void registerNamespaceSetProperty() {
        ReadableDataSource<String, Set<String>> namespaceDs = new NacosDataSource<>(properties, groupId,
                namespaceSetDataId, source -> JSON.parseObject(source, new TypeReference<Set<String>>() {
        }));
        ClusterServerConfigManager.registerNamespaceSetProperty(namespaceDs.getProperty());
    }

    private void initClientServerAssignProperty() {
        // Cluster map format:
        // [{"clientSet":["112.12.88.66@8729","112.12.88.67@8727"],"ip":"112.12.88.68","machineId":"112.12.88.68@8728","port":11111}]
        // machineId: <ip@commandPort>, commandPort for port exposed to Sentinel dashboard (transport module)
        ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(properties, groupId,
                clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {
            });
            return Optional.ofNullable(groupList)
                    .flatMap(this::extractClientAssignment)
                    .orElse(null);
        });
        ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
    }

    private void initStateProperty() {
        // Cluster map format:
        // [{"clientSet":["112.12.88.66@8729","112.12.88.67@8727"],"ip":"112.12.88.68","machineId":"112.12.88.68@8728","port":11111}]
        // machineId: <ip@commandPort>, commandPort for port exposed to Sentinel dashboard (transport module)
        ReadableDataSource<String, Integer> clusterModeDs = new NacosDataSource<>(properties, groupId,
                clusterMapDataId, source -> {
            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {
            });
            return Optional.ofNullable(groupList)
                    .map(this::extractMode)
                    .orElse(ClusterStateManager.CLUSTER_NOT_STARTED);
        });
        ClusterStateManager.registerProperty(clusterModeDs.getProperty());
    }

    private int extractMode(List<ClusterGroupEntity> groupList) {
        // If any server group machineId matches current, then it's token server.
        if (groupList.stream().anyMatch(this::machineEqual)) {
            return ClusterStateManager.CLUSTER_SERVER;
        }
        // If current machine belongs to any of the token server group, then it's token client.
        // Otherwise it's unassigned, should be set to NOT_STARTED.
        boolean canBeClient = groupList.stream()
                .flatMap(e -> e.getClientSet().stream())
                .filter(Objects::nonNull)
                .anyMatch(e -> e.equals(getCurrentMachineId()));
        return canBeClient ? ClusterStateManager.CLUSTER_CLIENT : ClusterStateManager.CLUSTER_NOT_STARTED;
    }

    private Optional<ServerTransportConfig> extractServerTransportConfig(List<ClusterGroupEntity> groupList) {
        return groupList.stream()
                .filter(this::machineEqual)
                .findAny()
                .map(e -> new ServerTransportConfig().setPort(e.getPort()).setIdleSeconds(600));
    }

    private Optional<ClusterClientAssignConfig> extractClientAssignment(List<ClusterGroupEntity> groupList) {
        if (groupList.stream().anyMatch(this::machineEqual)) {
            return Optional.empty();
        }
        // Build client assign config from the client set of target server group.
        for (ClusterGroupEntity group : groupList) {
            if (group.getClientSet().contains(getCurrentMachineId())) {
                String ip = group.getIp();
                Integer port = group.getPort();
                return Optional.of(new ClusterClientAssignConfig(ip, port));
            }
        }
        return Optional.empty();
    }

    private boolean machineEqual(/*@Valid*/ ClusterGroupEntity group) {
        return getCurrentMachineId().equals(group.getMachineId());
    }

    private String getCurrentMachineId() {
        // Note: this may not work well for container-based env.
        return HostNameUtil.getIp() + SEPARATOR + TransportConfig.getRuntimePort();
    }

    private static final String SEPARATOR = "@";


//
//
////    private void initClientConfigProperty() {
////        String APP_NAME = "sentinel-test";
////        String remoteAddress = "47.104.199.49:8848";
////        String groupId = "DEFAULT_GROUP";
////        String namespace ="2fde6910-51cd-4969-a846-50ed9e63cddb";
////        String clusterMapDataId = APP_NAME + "-cluster-map";
////        String configDataId = APP_NAME + "-cluster-client-config";
////        Properties properties = new Properties();
////        properties.setProperty("serverAddr",remoteAddress);
////        properties.setProperty("namespace",namespace);
////        ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(properties, groupId,
////                configDataId, source -> JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {}));
////        ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
////    }
//
//    private void initStateProperty() {
//        // Cluster map format:
//        // [{"clientSet":["112.12.88.66@8729","112.12.88.67@8727"],"ip":"112.12.88.68","machineId":"112.12.88.68@8728","port":11111}]
//        // machineId: <ip@commandPort>, commandPort for port exposed to Sentinel dashboard (transport module)
//
//        String APP_NAME = "sentinel-test";
//        String remoteAddress = "47.104.199.49:8848";
//        String groupId = "DEFAULT_GROUP";
//        String namespace ="2fde6910-51cd-4969-a846-50ed9e63cddb";
//        String clusterMapDataId = APP_NAME + "-cluster-map";
//        Properties properties = new Properties();
//        properties.setProperty("serverAddr",remoteAddress);
//        properties.setProperty("namespace",namespace);
//        ReadableDataSource<String, Integer> clusterModeDs = new NacosDataSource<>(properties, groupId,
//                clusterMapDataId, source -> {
//            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {});
//            return Optional.ofNullable(groupList)
//                    .map(this::extractMode)
//                    .orElse(ClusterStateManager.CLUSTER_NOT_STARTED);
//        });
//        ClusterStateManager.registerProperty(clusterModeDs.getProperty());
//    }
//    private int extractMode(List<ClusterGroupEntity> groupList) {
//        // If any server group machineId matches current, then it's token server.
//        if (groupList.stream().anyMatch(this::machineEqual)) {
//            return ClusterStateManager.CLUSTER_SERVER;
//        }
//        // If current machine belongs to any of the token server group, then it's token client.
//        // Otherwise it's unassigned, should be set to NOT_STARTED.
//        boolean canBeClient = groupList.stream()
//                .flatMap(e -> e.getClientSet().stream())
//                .filter(Objects::nonNull)
//                .anyMatch(e -> e.equals(getCurrentMachineId()));
//        return canBeClient ? ClusterStateManager.CLUSTER_CLIENT : ClusterStateManager.CLUSTER_NOT_STARTED;
//    }
//    private void initClientServerAssignProperty() {
//        String APP_NAME = "sentinel-test";
//        String remoteAddress = "47.104.199.49:8848";
//        String groupId = "DEFAULT_GROUP";
//        String namespace ="2fde6910-51cd-4969-a846-50ed9e63cddb";
//        String clusterMapDataId = APP_NAME + "-cluster-map";
//        Properties properties = new Properties();
//        properties.setProperty("serverAddr",remoteAddress);
//        properties.setProperty("namespace",namespace);
//        ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(properties, groupId,
//                clusterMapDataId, source -> {
//            List<ClusterGroupEntity> groupList = JSON.parseObject(source, new TypeReference<List<ClusterGroupEntity>>() {
//            });
//            return Optional.ofNullable(groupList)
//                    .flatMap(this::extractClientAssignment)
//                    .orElse(null);
//        });
//        ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
//    }
//
//    private Optional<ClusterClientAssignConfig> extractClientAssignment(List<ClusterGroupEntity> groupList) {
//        if (groupList.stream().anyMatch(this::machineEqual)) {
//            return Optional.empty();
//        }
//        // Build client assign config from the client set of target server group.
//        for (ClusterGroupEntity group : groupList) {
//            if (group.getClientSet().contains(getCurrentMachineId())) {
//                String ip = group.getIp();
//                Integer port = group.getPort();
//                return Optional.of(new ClusterClientAssignConfig(ip, port));
//            }
//        }
//        return Optional.empty();
//    }
//
//    private boolean machineEqual(/*@Valid*/ ClusterGroupEntity group) {
//        return getCurrentMachineId().equals(group.getMachineId());
//    }
//
//    private String getCurrentMachineId() {
//        // Note: this may not work well for container-based env.
//        return HostNameUtil.getIp() + SEPARATOR + TransportConfig.getRuntimePort();
//    }
//
//    private static final String SEPARATOR = "@";

}
